/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "ScanMethod4PingBcast.h"

//--------------------------------------------------------------------

ScanMethod4PingBcast::ScanMethod4PingBcast(void) : ScanMethod()
{
	myType = __wmt4PingBcast;
}

ScanMethod4PingBcast::~ScanMethod4PingBcast(void)
{
}

//--------------------------------------------------------------------
int ScanMethod4PingBcast::InternalInit(void)
{
	CString str, sss;
	in_addr ia;

	if(workPhase!=__wptDiscovery) return MSR_ERR_PHASE;

	FillRandomBuffer();

	dstIpAddress = 0;
	
	str = scanParameters.Trim();
	if(str.IsEmpty())					  dstIpAddress = 0xFFFFFFFF;
	else{
		if(str.CompareNoCase(_T("B"))==0) dstIpAddress = 0xFFFFFFFF;
		if(str.CompareNoCase(_T("L"))==0) dstIpAddress = winPcap->GetLocalIPBroadcastAddress();
		if(str.CompareNoCase(_T("H"))==0) dstIpAddress = 0x010000E0;	// 224.0.0.1
		if(str.CompareNoCase(_T("R"))==0) dstIpAddress = 0x020000E0;	// 224.0.0.2

		if(dstIpAddress==0){
			CT2A cStr(str);
			ia.S_un.S_addr = inet_addr(cStr);
			sss = inet_ntoa(ia);
			if(sss.CompareNoCase(str)==0)
				dstIpAddress = ia.S_un.S_addr;
		}
	}

	if(dstIpAddress==0) return MSR_ERR_PARAMETER;

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------
int ScanMethod4PingBcast::InternalExecute(void)
{
	ScanPacket* pack;
	bool res;

	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	pack = FindFirstUnusedPacket();
	while(pack==NULL){
		SleepEx(waitTime, TRUE);
		CheckResponses();
		CheckDeaths();
		pack = FindFirstUnusedPacket();
	}

	if(pack!=NULL){
		pack->Initialize(true);
		pack->sTime       = GetTickCount();
		pack->addr        = 0;
		pack->port        = 0;
		pack->methodType  = myType;
		pack->fullAddress = NULL;
		pack->machineInfo = NULL;

		res = SendIPv4PingBroadcast(pack);
		if(!res)
			pack->Initialize(false);
	}
	else res = false;

	while(PacketsUsed()>0){
		CheckResponses();
		CheckDeaths();
		SleepEx(1, TRUE);
	}

	if(hWnd!=NULL)
		PostMessage(hWnd, CHS_MSG_PACKETS_SENT, (WPARAM)0, (LPARAM)packSent);

	if(!res) return MSR_ERR_FAILED;

	return MSR_ERR_OK;
}

//--------------------------------------------------------------------

void ScanMethod4PingBcast::ComputeRandomValues(void)
{
	wIcmpId = (WORD)(GetRandomValue() & 0x0000FFFF);
}

bool ScanMethod4PingBcast::SendIPv4PingBroadcast(ScanPacket* scanPackIn)
{
	BYTE srcMac[6];
	BYTE dstMac[6];
	bool res;

	if(scanPackIn==NULL) return false; // nothing to send

	ComputeRandomValues();

	scanPackIn->paramDW = (DWORD)wIcmpId;

	res = packetBuilderEx->BuildPackIcmpEchoRequestWithId(winPcap->GetLocalIP(), dstIpAddress, wIcmpId);
	if(!res) return false;

	winPcap->FillSrcMAC(&srcMac[0]);

	if(packetBuilderEx->IsIPv4MulticastAddress(dstIpAddress)){
		res = packetBuilderEx->AddEthernetHeaderForIPMulticast(srcMac, dstIpAddress, 0x0800);
	}
	else{
		dstMac[0] = 0xFF;
		dstMac[1] = 0xFF;
		dstMac[2] = 0xFF;
		dstMac[3] = 0xFF;
		dstMac[4] = 0xFF;
		dstMac[5] = 0xFF;
		res = packetBuilderEx->AddEthernetHeader(srcMac, dstMac, 0x0800);
	}
	if(!res) return false;

	return SendPacket(packetBuilderEx->GetPacket(), (int)packetBuilderEx->GetPacketSize());
}

//--------------------------------------------------------------------

void ScanMethod4PingBcast::ProcessEchoReplyEx(BYTE* dataIn, int sizeIn, BYTE* srcMAC)
{
	ScanPacket    *pack;
	IPHeaderPG    *iph;
	ICMPHeaderPG  *icmph;
	int           dataOffset;
	MMachineInfo  mach;
	MFullAddress  addr;
	in_addr       inAddr;
	WORD          wIcmpId;
	int           i;

	if(sizeIn<22) return;

	pack = FindPacket(0, 0, myType);
	if(pack==NULL) return;

	iph = (IPHeaderPG*)dataIn;
	dataOffset  = (int)(iph->HLV & 0x0F);
	dataOffset *= 4;
	if(sizeIn<(dataOffset + 8)){
		// incomplete packet
		return;
	}

	icmph = (ICMPHeaderPG*)&dataIn[dataOffset];
	wIcmpId = (WORD)pack->paramDW;
	if(icmph->Id!=wIcmpId){
		// not my packet
		return;
	}

	inAddr.S_un.S_un_b.s_b1 = iph->Source[0];
	inAddr.S_un.S_un_b.s_b2 = iph->Source[1];
	inAddr.S_un.S_un_b.s_b3 = iph->Source[2];
	inAddr.S_un.S_un_b.s_b4 = iph->Source[3];

	// add address srcIP:srcMAC

	mach.Initialize();
	addr.Initialize();

	for(i=0; i<6; i++)
		addr.addrMAC.addr[i] = srcMAC[i];

	addr.addrIP.Set(inAddr.S_un.S_addr);

	mach.address.LoadFromPointer(&addr);

	machInfoList->Add(&mach);
}

//--------------------------------------------------------------------
